﻿/*
 * Cratos Tools - General
 * Cratos Collection Base class
 * 
 * Copyright (c) 2011-2012 Draco
 * 
 * This file is part of Cratos.
 * 
 * Cratos is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Cratos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cratos.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *       .==.        .==.
 *      //'^\\      //^'\\
 *     // ^ ^\(\__/)/^ ^^\\
 *    //^ ^^ ^/6  6\ ^^ ^ \\
 *   //^ ^^ ^/( .. )\^ ^ ^ \\
 *  // ^^ ^/\| V""V|/\^ ^ ^ \\
 * //^^/\/ /  '~~'  \ \/\^ ^ \\
 *  ------------------------------------
 *  HERE BE DRAGONS
 *  
 *  Johan Dercksen - johan@draco.co.za
 */
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Cratos.Tools.General;
using System.Reflection;
using Cratos.Tools.DataObject;

namespace Cratos.Server.Entity
{
	/// <summary>
	/// Cratos Collection Base class
	/// </summary>
	public abstract class CratosCollectionBase<T> : CratosEntityDatabase, IDisposable where T : IDOCratos, new()
	{
		#region Class Constant declarations
		#endregion

		#region Class Event declarations
		#endregion

		#region Class Variable declarations
		private bool										isDisposed;
		private Collection<CratosError> entityErrors = new Collection<CratosError>();
		#endregion

		#region Class Constructors & Destructors
		/// <summary>
		/// Class Constructor
		/// </summary>
		public CratosCollectionBase() : base() { }

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or
		/// resetting unmanaged resources.
		/// </summary>
		public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }

		/// <summary>
		/// Dispose method doing the disposing
		/// </summary>
		/// <param name="isDisposing"></param>
		protected virtual void Dispose(bool isDisposing)
		{
			// Declare the required variables

			// Check to see if Dispose has already been called.
			if (!this.isDisposed)
			{
				// Dispose the objects if required
				if (isDisposing)
				{
					// Dispose of the required objects
				}
			}

			// Set the disposed indicator
			this.isDisposed = true;

			// Return to calling method
			return;
		}
		#endregion

		#region Class Properties
		/// <summary>
		/// Ignore Unity Indicator
		/// </summary>
		public bool IgnoreUnity { get; set; }

		/// <summary>
		/// Cratos Entity Error Collection
		/// </summary>
		public Collection<CratosError> Errors
		{
			get { return (this.entityErrors); }
		}
		#endregion

		#region Class Methods
		/// <summary>
		/// Process Embedded Object
		/// </summary>
		/// <param name="dataObject">Data Object definition</param>
		/// <param name="parentDOObject">Current Embedded Data Object</param>
		/// <param name="currentEmbeddedAttribute">Embedded Object Attribute</param>
		/// <returns>A boolean indicating the success of the method</returns>
		protected bool ProcessEmbeddedObject(IDOCratos dataObject, IDOCratos parentDOObject,
																				 DOCratosEmbeddedAttribute currentEmbeddedAttribute)
		{
			// Declare the required variables
			bool														returnValue      = false;
			IDOCratos												embeddedDOObject = null;
			DOCratosEmbeddedKeyAttribute[]	allKeyAttributes;
			PropertyInfo										embeddedDOProperty, parentProperty, embeddedProperty;

			try
			{
				// Retrieve the Embedded Data Object
				embeddedDOProperty = parentDOObject.GetType().GetProperty(currentEmbeddedAttribute.Property);
				embeddedDOObject   = (IDOCratos)dataObject.Clone();

				// Make sure we have data to work with
				if (embeddedDOObject == null)
				{
					// Add an error to the Error Collection
					this.Errors.Add(new CratosError("CratosEntityCollection.ProcessEmbeddedObject", CratosLogSeverity.ValidationError,
																					string.Format("Embedded Object or Property not found [{0} - {1}]",
																												parentDOObject.GetType(), currentEmbeddedAttribute.Name)));
				}
				else if (embeddedDOObject.DOAction != DOCratosAction.None)
				{
					#region Parameter Processing
					// Retrieve the Key Attributes
					allKeyAttributes = (DOCratosEmbeddedKeyAttribute[])embeddedDOProperty.GetCustomAttributes(typeof(DOCratosEmbeddedKeyAttribute), false);

					// Process all the Keys
					foreach (DOCratosEmbeddedKeyAttribute currentKey in allKeyAttributes)
					{
						// Retrieve the relevant property information
						embeddedProperty = embeddedDOObject.GetType().GetProperty(currentKey.EmbeddedKeyProperty);
						parentProperty   = parentDOObject.GetType().GetProperty(currentKey.ParentProperty);

						// Make sure both properties exist
						if ((embeddedProperty != null) && (parentProperty != null))
						{
							// Set the Embedded object key property values
							embeddedProperty.SetValue(embeddedDOObject, parentProperty.GetValue(parentDOObject, null), null);
						}
						else
						{
							// Add an error to the Error Collection
							this.Errors.Add(new CratosError("CratosEntityCollection.ProcessEmbeddedObject", CratosLogSeverity.ValidationError,
																							string.Format("Key Properties for embedded object {0} is invalid",
																														currentEmbeddedAttribute.Name)));
						}
					}
					#endregion

					// Perform the requested action
					if (embeddedDOObject.PerformAction()) { returnValue = true; }
					else
					{
						// Process any errors
						foreach (CratosError currentError in embeddedDOObject.Errors) { this.Errors.Add(currentError); }
					}

					// Update the Parent Data Object
					DOCratosHelper.SetPropertyValue(parentDOObject, currentEmbeddedAttribute.Property, embeddedDOObject);
				}
			}
			catch (Exception runtimeException)
			{
				// Create and setup a new error
				this.Errors.Add(new CratosError("CratosEALBase.ProcessEmbeddedObject", CratosLogSeverity.Exception,
																				"Runtime error ocurred", runtimeException));
			}

			// Return to calling method
			return (returnValue);
		}

		/// <summary>
		/// Process Embedded Collection
		/// </summary>
		/// <param name="doCollection">Data Object definition</param>
		/// <param name="parentDOObject">Parent Data Object</param>
		/// <param name="currentEmbeddedAttribute">Embedded Object Attribute</param>
		/// <returns>A boolean indicating the success of the method</returns>
		protected bool ProcessEmbeddedCollection(object doCollection, IDOCratos parentDOObject,
																						 DOCratosEmbeddedAttribute currentEmbeddedAttribute)
		{
			// Declare the required variables
			bool														returnValue = false;
			object													sourceData;
			DOCratosEmbeddedKeyAttribute[]	embeddedKeyAttributes;

			try
			{
				// Retrieve the relevant object data
				embeddedKeyAttributes = (DOCratosEmbeddedKeyAttribute[])parentDOObject.GetType().GetProperty(currentEmbeddedAttribute.Property).GetCustomAttributes(typeof(DOCratosEmbeddedKeyAttribute), false);

				// Perform the relevant action if required
				if (((DOCratosCollectionAction)DOCratosHelper.GetPropertyValue(doCollection, "CollectionAction")) != DOCratosCollectionAction.None)
				{
					#region Parameter / Filter Processing
					// Set the relevant Embedded Key data
					foreach (DOCratosEmbeddedKeyAttribute currentKey in embeddedKeyAttributes)
					{
						// Retrieve the relevant Source Data
						sourceData = parentDOObject.GetType().GetProperty(currentKey.ParentProperty).GetValue(parentDOObject, null);

						// Update the Key Property
						DOCratosHelper.SetPropertyValue(doCollection, currentKey.EmbeddedKeyProperty, sourceData);
					}
					#endregion

					// Send the request and handle any errors
					if (!((bool)DOCratosHelper.InvokeMethod(doCollection, "PerformAction", null)))
					{
						foreach (CratosError currentError in ((IEnumerable<object>)DOCratosHelper.GetPropertyValue(doCollection, "Errors")))
						{
							this.Errors.Add(currentError);
						}
					}
				}
			}
			catch (Exception runtimeException)
			{
				// Create and setup a new error
				this.Errors.Add(new CratosError("CratosEntityCollection.ProcessEmbeddedCollection", CratosLogSeverity.Exception,
																				"Runtime error ocurred", runtimeException));
			}

			// Return to calling method
			return (returnValue);
		}
		#endregion

		#region Class Support Methods
		#endregion
	}
}
